﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using NUnit.Framework;
using Obra.Api;
using System.ServiceModel.Web;
using Obra.SmugMug.Api;

namespace Obra.Test
{
    /// <summary>
    /// Test the deserialization of SmugMug XML responses.
    /// </summary>
    /// <remarks>
    /// <para>
    /// Test the XML annotations on the various classes in the SmugMug assembly.
    /// </para>
    /// </remarks>
    [TestFixture]
    public class DeserializeTester
    {
        [SetUp]
        public void SetUp()
        {
        }

        [TearDown]
        public void TearDown()
        {
        }

        [TestFixtureSetUp]
        public void TestFixtureSetUp()
        {
            XmlReaderSettings xmlReaderSettings = new XmlReaderSettings ();

            //_projectSchema = GetSmugMugSchema();

            //// Add the Project.xsd schema from this assembly.
            //xmlReaderSettings.Schemas.Add(
            //    _projectSchema);
            //xmlReaderSettings.Schemas.Compile ();
            //xmlReaderSettings.ValidationType = ValidationType.Schema;

            this.xmlReaderSettings = xmlReaderSettings;
        }

        private XmlReaderSettings xmlReaderSettings;

        //private XmlSchemat _projectSchema;

        public static XmlSchema GetSmugMugSchema<TObject> ()
        {
            return XmlSchema.Read (
                typeof (TObject).Assembly.GetManifestResourceStream (typeof (TObject), "SmugMug.xsd"),
                null);
        }

        private TObject Deserialize<TObject>(
            XmlReader reader)
        {
            using (var wrappingReader = XmlReader.Create(reader, this.xmlReaderSettings))
            {
                XmlSerializer serializer = GetXmlSerializer(
                    typeof(TObject));
                
                return (TObject)serializer.Deserialize(
                    wrappingReader);
            }
        }

        private TObject DeserializeFromString<TObject>(
            string xml)
        {
            return this.Deserialize<TObject>(
                XmlReader.Create(new StringReader(xml)));
        }

        private TObject DeserializeFromBytes<TObject>(
            byte[] xml)
        {
            return this.Deserialize<TObject>(
                XmlReader.Create(new StreamReader(new MemoryStream(xml))));
        }

        private TObject DeserializeFromFile<TObject>(
            string filename)
        {
            return this.DeserializeFromString<TObject>(
                File.ReadAllText(filename));
        }

        private Dictionary<Type, XmlSerializer> xmlSerializerTypeCache =
            new Dictionary<Type, XmlSerializer>();

        private XmlSerializer GetXmlSerializer(
            Type type)
        {
            if (!this.xmlSerializerTypeCache.ContainsKey(type))
            {
                this.CreateXmlSerializer(type);
            }

            return this.xmlSerializerTypeCache[type];
        }

        private void CreateXmlSerializer(
            Type type)
        {
            this.xmlSerializerTypeCache[type] = new XmlSerializer(type);
        }

        //public void Foo()
        //{
        //    ServiceHost serviceHost = new ServiceHost(
        //        typeof(SmugMugServer),
        //        new Uri("http://localhost:9876/smugmug"));

        //    serviceHost.AddServiceEndpoint(
        //        typeof(ISmugMug),
        //        new WSHttpBinding (SecurityMode.None),
        //        String.Empty);

        //    ContractDescription contractDescription = 
        //        serviceHost.Description.Endpoints[0].Contract;

        //    OperationDescription operationDescription = 
        //        contractDescription.Operations.Find("LoginWithPassword");

        //    XmlSerializerOperationBehavior serializerBehavior =
        //        operationDescription.Behaviors.Find<XmlSerializerOperationBehavior>();

        //    if (serializerBehavior == null)
        //    {
        //        serializerBehavior = new XmlSerializerOperationBehavior(operationDescription);
        //        operationDescription.Behaviors.Add(serializerBehavior);
        //    }

        //    serializerBehavior.XmlSerializerFormatAttribute.Style = OperationFormatStyle.Document;

        //    //serviceHost.Open();
        //    // Listening...
        //    //serviceHost.Close();
        //}

        public void Foo()
        {
            using (WebServiceHost host = new WebServiceHost(
                typeof(SmugMugServer),
                new Uri(Messages.TestUrl)))
            {
                host.Open();
            }

            
            //ContractDescription contractDescription =
            //    serviceHost.Description.Endpoints[0].Contract;

            //OperationDescription operationDescription =
            //    contractDescription.Operations.Find("LoginWithPassword");

            //XmlSerializerOperationBehavior serializerBehavior =
            //    operationDescription.Behaviors.Find<XmlSerializerOperationBehavior>();

            //if (serializerBehavior == null)
            //{
            //    serializerBehavior = new XmlSerializerOperationBehavior(operationDescription);
            //    operationDescription.Behaviors.Add(serializerBehavior);
            //}
            //serializerBehavior.XmlSerializerFormatAttribute.Style = OperationFormatStyle.Document;

            //ServiceDescription description = serviceHost.Description;

            //serviceHost.IncrementManualFlowControlLimit(100);
            //serviceHost.Open();
            //// Listening...

            //IChannelListener icl = serviceHost.ChannelDispatchers[0].Listener;
            //ChannelDispatcher dispatcher = new ChannelDispatcher(icl);
            //ChannelDispatcherCollection dispatchers = serviceHost.ChannelDispatchers;

            //foreach (ChannelDispatcher disp in dispatchers)
            //{
            //}

            //serviceHost.Close();
        }

        [Test]
        public void SmugMugLoginWithPassword()
        {
            LoginWithPasswordContract response = this.DeserializeFromFile<LoginWithPasswordContract>(
                @"..\..\SmugMug\sample\LoginWithPassword.xml");

            Assert.AreEqual("$1$.Ui2accR$asqKM9FBQWmI3ASDeeOIs8", response.PasswordHash);
            Assert.AreEqual(AccountType.Pro, response.AccountType);
            Assert.AreEqual(25165824, response.FileSizeLimit);
            Assert.AreEqual(0, response.SmugVault);
            Assert.AreEqual("c525168360b919d19135993990a992bd", response.Session.Id);
            Assert.AreEqual(response.SessionId, response.SessionId);
            Assert.AreEqual("example", response.User.DisplayName);
            Assert.AreEqual(1234, response.User.Id);
            Assert.AreEqual("example", response.User.NickName);
        }

        [Test]
        public void SmugMugAlbums()
        {
            AlbumsContract response = this.DeserializeFromFile<AlbumsContract>(
                @"..\..\SmugMug\sample\AlbumsGet.xml");

            Assert.AreEqual(3, response.Albums.Count);

            // -- Album[0] --
            AlbumContract album1 = response.Albums[0];
            Assert.AreEqual(1000, album1.Id);

            Assert.AreEqual(0, album1.Highlight.Id);
            Assert.AreEqual(0, album1.Template.Id);
            Assert.AreEqual(0, album1.Watermark.Id);
            Assert.AreEqual("Swimming", album1.Category.Name);
            Assert.AreEqual(159294, album1.Category.Id);

            // -- Album[1] --
            AlbumContract album2 = response.Albums[1];
            Assert.AreEqual(1001, album2.Id);
            
            // -- Album[2] --
            AlbumContract album3 = response.Albums[2];
            Assert.AreEqual(1002, album3.Id);
        }

        [Test]
        public void SmugMugImages()
        {
            ImagesContract response = this.DeserializeFromFile<ImagesContract>(
                @"..\..\SmugMug\sample\ImagesGet.xml");

            Assert.AreEqual(5, response.Images.Count);

            ImageContract image = response.Images[0];

            Assert.AreEqual(50071990, image.Id);
            Assert.AreEqual("VXJ9u", image.Key);

            // Dates
            Assert.AreEqual("2008-06-24 04:57:45", image.DateString);
            Assert.AreEqual("2008-06-24 04:58:39", image.LastUpdatedString);

            // URLs
            Assert.AreEqual ("http://example.smugmug.com/gallery/3003_FxWoe#50071990_VXJ9u", image.AlbumUrl);
            Assert.AreEqual("http://example.smugmug.com/photos/50071990_VXJ9u-O.jpg", image.OriginalUrl);
            Assert.AreEqual("http://example.smugmug.com/photos/50071990_VXJ9u-X3.jpg", image.X3LargeUrl);
            Assert.AreEqual("http://example.smugmug.com/photos/50071990_VXJ9u-X2.jpg", image.X2LargeUrl);
            Assert.AreEqual("http://example.smugmug.com/photos/50071990_VXJ9u-XL.jpg", image.XLargeUrl);
            Assert.AreEqual("http://example.smugmug.com/photos/50071990_VXJ9u-L.jpg", image.LargeUrl);
            Assert.AreEqual("http://example.smugmug.com/photos/50071990_VXJ9u-M.jpg", image.MediumUrl);
            Assert.AreEqual("http://example.smugmug.com/photos/50071990_VXJ9u-S.jpg", image.SmallUrl);
            Assert.AreEqual("http://example.smugmug.com/photos/50071990_VXJ9u-Ti.jpg", image.TinyUrl);
            Assert.AreEqual("http://example.smugmug.com/photos/50071990_VXJ9u-Th.jpg", image.ThumbUrl);

            Assert.IsNull(image.Video320Url);
            Assert.IsNull(image.Video640Url);
            Assert.IsNull(image.Video960Url);
            Assert.IsNull(image.Video1280Url);

            // File
            Assert.AreEqual("IMG_7770.JPG", image.FileName);
            Assert.AreEqual(Format.JPG.ToString(), image.Format);
            Assert.AreEqual("ae64a5dc4a8ec33e896a6f43bfc4e4db", image.MD5Sum);
            Assert.AreEqual("ae64a5dc4a8ec33e896a6f43bfc4e4db", image.Checksum);
            
            // Image
            Assert.AreEqual(2304, image.Height);
            Assert.AreEqual(3072, image.Width);
            Assert.AreEqual(0, image.Serial);
            Assert.AreEqual(3302937, image.Size);
            Assert.AreEqual(1, image.Position);
            Assert.AreEqual("0", image.Watermark);
            Assert.IsFalse(image.Hidden);
            Assert.IsEmpty(image.Caption);
            Assert.IsEmpty(image.Keywords);

            foreach (ImageContract img in response.Images)
            {
                Assert.AreEqual ("FxWoe", img.Album.Key);
                Assert.AreEqual (3003, img.Album.Id);
            }
        }

        [Test]
        public void SmugMugCategories()
        {
            CategoriesContract response = this.DeserializeFromFile<CategoriesContract>(
                @"..\..\SmugMug\sample\CategoriesGet.xml");

            Assert.AreEqual(69, response.Category.Count);

            Dictionary<string, int> categories = new Dictionary<string, int>();

            foreach (CategoryContract cat in response.Category)
            {
                categories[cat.Title] = cat.Id;
            }

            Assert.AreEqual(0, categories["Other"]);
            Assert.AreEqual(21, categories["Trucks"]);
            Assert.AreEqual(40, categories["Electronics"]);
            Assert.AreEqual(57, categories["Food"]);

            Assert.IsFalse(categories.ContainsKey("NonExistentValue"));
        }

        [Test]
        public void SmugMugSubCategories()
        {
        }


        [Test]
        public void SmugMugAlbumTemplates()
        {
            AlbumTemplatesContract response = this.DeserializeFromFile<AlbumTemplatesContract>(
              @"..\..\SmugMug\sample\AlbumTemplatesGet.xml");

            Assert.AreEqual(2, response.AlbumTemplate.Count);

            Assert.AreEqual("smugmug default", response.AlbumTemplate[0].Name);
            Assert.AreEqual("default", response.AlbumTemplate[1].Name);

            // Album Template: smugmug default
            AlbumTemplateContract albumTemplate = response.AlbumTemplate[0];
            Assert.AreEqual(0, albumTemplate.Template.Id);
            Assert.AreEqual(0, albumTemplate.Community.Id);
            Assert.AreEqual(0, albumTemplate.Watermark.Id);
            Assert.AreEqual(2, albumTemplate.Id);
            Assert.IsEmpty(albumTemplate.Backprinting);
            Assert.IsTrue(albumTemplate.CanRank);
            Assert.IsFalse(albumTemplate.Clean);
            Assert.IsTrue(albumTemplate.Comments);
            Assert.IsTrue(albumTemplate.DefaultColor);
            Assert.IsTrue(albumTemplate.Exif);
            Assert.IsFalse(albumTemplate.FamilyEdit);
            Assert.IsFalse(albumTemplate.FileNames);
            Assert.IsFalse(albumTemplate.FriendEdit);
            Assert.IsTrue(albumTemplate.Geography);
            Assert.IsFalse(albumTemplate.Header);
            Assert.IsFalse(albumTemplate.HideOwner);
            Assert.IsTrue(albumTemplate.Larges);
            Assert.IsTrue(albumTemplate.Originals);
            Assert.IsEmpty(albumTemplate.Password);
            Assert.IsEmpty(albumTemplate.PasswordHint);
            Assert.IsTrue(albumTemplate.Printable);
            Assert.AreEqual(0, albumTemplate.ProofDays);
            Assert.IsFalse(albumTemplate.Protected);
            Assert.IsTrue(albumTemplate.Public);
            Assert.IsTrue(albumTemplate.Share);
            Assert.IsTrue(albumTemplate.SmugSearchable);
            Assert.AreEqual(SortDirection.Ascending, albumTemplate.SortDirection);
            Assert.AreEqual(SortMethod.Position, albumTemplate.SortMethod);
            Assert.IsTrue(albumTemplate.SquareThumbs);
            Assert.AreEqual((float)0.2, albumTemplate.UnsharpAmount);
            Assert.AreEqual((float)1, albumTemplate.UnsharpRadius);
            Assert.AreEqual((float)1, albumTemplate.UnsharpSigma);
            Assert.AreEqual((float)0.05, albumTemplate.UnsharpThreshold);
            Assert.IsFalse(albumTemplate.Watermarking);
            Assert.IsTrue(albumTemplate.WorldSearchable);
            Assert.IsTrue(albumTemplate.XLarges);
            Assert.IsTrue(albumTemplate.X2Larges);
            Assert.IsTrue(albumTemplate.X3Larges);
        }

        [Test]
        public void SmugMugImageExif()
        {
            ImageExif response = this.DeserializeFromFile<ImageExif>(
                @"..\..\SmugMug\sample\ImageExif.xml");

            Assert.AreEqual("f/2.8", response.Aperture);
            Assert.IsTrue(String.IsNullOrEmpty(response.Brightness));
            Assert.AreEqual("5mm", response.CcdWidth);
            Assert.AreEqual("1", response.ColorSpace);
            Assert.AreEqual("5/1", response.CompressedBitsPerPixel);
            Assert.AreEqual(0, response.Contrast);

            Assert.AreEqual("2009-09-18 18:27:43", response.DateTimeString);
            Assert.AreEqual(2009, response.DateTime.Year);
            Assert.AreEqual(9, response.DateTime.Month);
            Assert.AreEqual(18, response.DateTime.Day);
            Assert.AreEqual(22, response.DateTime.Hour);
            Assert.AreEqual(27, response.DateTime.Minute);
            Assert.AreEqual(43, response.DateTime.Second);

            Assert.AreEqual("2009-09-18 18:27:43", response.DateTimeDigitizedString);
            Assert.AreEqual(2009, response.DateTimeDigitized.Year);
            Assert.AreEqual(9, response.DateTimeDigitized.Month);
            Assert.AreEqual(18, response.DateTimeDigitized.Day);
            Assert.AreEqual(22, response.DateTimeDigitized.Hour);
            Assert.AreEqual(27, response.DateTimeDigitized.Minute);
            Assert.AreEqual(43, response.DateTimeDigitized.Second);

            Assert.AreEqual("2009-09-18 18:27:43", response.DateTimeOriginalString);
            Assert.AreEqual(2009, response.DateTimeOriginal.Year);
            Assert.AreEqual(9, response.DateTimeOriginal.Month);
            Assert.AreEqual(18, response.DateTimeOriginal.Day);
            Assert.AreEqual(22, response.DateTimeOriginal.Hour);
            Assert.AreEqual(27, response.DateTimeOriginal.Minute);
            Assert.AreEqual(43, response.DateTimeOriginal.Second);

            Assert.AreEqual("3264/3264", response.DigitalZoomRatio);
            Assert.AreEqual("0/3", response.ExposureBiasValue);
            Assert.AreEqual(0, response.ExposureMode);
            Assert.AreEqual(0, response.ExposureProgram);
            Assert.AreEqual("1/60", response.ExposureTime);
            Assert.AreEqual(89, response.Flash);
            Assert.AreEqual("6200/1000", response.FocalLength);
            Assert.AreEqual(0, response.FocalLengthIn35mmFilm);
            Assert.AreEqual(1001, response.Id);
            Assert.AreEqual(80, response.Iso);
            Assert.AreEqual(0, response.LightSource);
            Assert.AreEqual("Canon", response.Make);
            Assert.AreEqual(0, response.Metering);
            Assert.AreEqual("Canon PowerShot SD1100 IS", response.Model);
            Assert.AreEqual(0, response.Saturation);
            Assert.AreEqual(2, response.SensingMethod);
            Assert.AreEqual(0, response.Sharpness);
            Assert.IsTrue(String.IsNullOrEmpty(response.SubjectDistance));
            Assert.AreEqual(0, response.SubjectDistanceRange);
            Assert.AreEqual(0, response.WhiteBalance);

            ImageExif emptyResponse = this.DeserializeFromFile<ImageExif>(
                @"..\..\SmugMug\sample\ImageExifEmpty.xml");

            Assert.IsTrue(String.IsNullOrEmpty(emptyResponse.Aperture));
            Assert.IsTrue(String.IsNullOrEmpty(emptyResponse.Brightness));
            Assert.IsTrue(String.IsNullOrEmpty(emptyResponse.CcdWidth));
            Assert.IsTrue(String.IsNullOrEmpty(emptyResponse.ColorSpace));
            Assert.IsTrue(String.IsNullOrEmpty(emptyResponse.CompressedBitsPerPixel));
            Assert.AreEqual(0, emptyResponse.Contrast);
            Assert.IsTrue(String.IsNullOrEmpty(emptyResponse.DateTimeString));
            Assert.IsTrue(String.IsNullOrEmpty(emptyResponse.DateTimeDigitizedString));
            Assert.IsTrue(String.IsNullOrEmpty(emptyResponse.DateTimeOriginalString));
            Assert.IsTrue(String.IsNullOrEmpty(emptyResponse.DigitalZoomRatio));
            Assert.IsTrue(String.IsNullOrEmpty(emptyResponse.ExposureBiasValue));
            Assert.AreEqual(0, emptyResponse.ExposureMode);
            Assert.AreEqual(0, emptyResponse.ExposureProgram);
            Assert.IsTrue(String.IsNullOrEmpty(emptyResponse.ExposureTime));
            Assert.AreEqual(0, emptyResponse.Flash);
            Assert.IsTrue(String.IsNullOrEmpty(emptyResponse.FocalLength));
            Assert.AreEqual(0, emptyResponse.FocalLengthIn35mmFilm);
            Assert.AreEqual(1001, emptyResponse.Id);
            Assert.AreEqual(0, emptyResponse.Iso);
            Assert.AreEqual(0, emptyResponse.LightSource);
            Assert.IsTrue(String.IsNullOrEmpty(emptyResponse.Make));
            Assert.AreEqual(0, emptyResponse.Metering);
            Assert.IsTrue(String.IsNullOrEmpty(emptyResponse.Model));
            Assert.AreEqual(0, emptyResponse.Saturation);
            Assert.AreEqual(0, emptyResponse.SensingMethod);
            Assert.AreEqual(0, emptyResponse.Sharpness);
            Assert.IsTrue(String.IsNullOrEmpty(emptyResponse.SubjectDistance));
            Assert.AreEqual(0, emptyResponse.SubjectDistanceRange);
            Assert.AreEqual(0, emptyResponse.WhiteBalance);
        }
    }
}
